﻿using System;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using Notoric.Gui.Windows;

namespace Notoric.Gui.Commands
{
    /// <summary>
    /// Implements a command that binds a property of a specified control
    /// to a specified property of the current text selection.<br/>
    /// When this command is executed, it sets the formating property
    /// on the current selection. When the current selection changes,
    /// it sets the corresponding property on the control.
    /// </summary>
    public class RtfSelectionPropertySetterCommand : RtfTextSelectionCommand
    {
        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="RtfSelectionPropertySetterCommand"/> class.
        /// </summary>
        /// <param name="mainWindow">
        /// <para>
        /// Type: <see cref="MainWindow"/>
        /// </para>
        /// <para>
        /// The main application window.
        /// </para>
        /// </param>
        /// <param name="selectionProperty">
        /// <para>
        /// Type: <see cref="DependencyProperty"/>
        /// </para>
        /// <para>
        /// The format property that is being set by this command.
        /// </para>
        /// </param>
        /// <param name="controlProperty">
        /// <para>
        /// Type: <see cref="DependencyProperty"/>
        /// </para>
        /// <para>
        /// The property of the <paramref name="targetControl"/> that is being controlled by this command.
        /// </para>
        /// </param>
        /// <param name="targetControl">
        /// <para>
        /// Type: <see cref="Control"/>
        /// </para>
        /// <para>
        /// The control that is being controlled by this command.
        /// </para>
        /// </param>
        /// <param name="converter">
        /// <para>
        /// Type: <see cref="IValueConverter"/>
        /// </para>
        /// <para>
        /// The object that converts the <paramref name="controlProperty"/> value to <paramref name="selectionProperty"/> value and back.
        /// </para>
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// The <paramref name="mainWindow"/>, <paramref name="selectionProperty"/>, <paramref name="controlProperty"/> 
        /// or <paramref name="targetControl"/> is <see langword="null"/>.
        /// </exception>
        public RtfSelectionPropertySetterCommand(MainWindow mainWindow, DependencyProperty selectionProperty, DependencyProperty controlProperty, Control targetControl,
            IValueConverter converter)
            : base(mainWindow)
        {
            if (selectionProperty == null)
                throw new ArgumentNullException("selectionProperty");
            if (controlProperty == null)
                throw new ArgumentNullException("controlProperty");
            if (targetControl == null)
                throw new ArgumentNullException("targetControl");

            this.selectionProperty = selectionProperty;
            this.control = targetControl;
            this.controlProperty = controlProperty;
            this.converter = converter;
        }

        #endregion

        #region RtfTextSelectionCommand Members

        /// <inheritdoc/>
        protected override bool CanExecuteCore(object parameter)
        {
            return interior != null && interior.Selection != null;
        }

        /// <inheritdoc/>
        protected override void SelectionChanged(ViewModel.Elements.RtfTextSectionInterior interior)
        {
            this.interior = interior;
            if (interior != null)
            {
                var selection = interior.Selection;
                var value = (selection != null ? selection.GetPropertyValue(selectionProperty) : null);
                if (converter != null)
                    value = converter.ConvertBack(value, controlProperty.PropertyType, null, CultureInfo.CurrentCulture);

                IsWritingPropertyBack = true;
                control.SetValue(controlProperty, value);
                IsWritingPropertyBack = false;
            }
            base.SelectionChanged(interior);
        }

        #endregion

        #region ICommand Members

        /// <inheritdoc/>
        public override void Execute(object parameter)
        {
            if (IsWritingPropertyBack || interior == null || interior.Selection == null)
                return;

            var selection = interior.Selection;
            var value = control.GetValue(controlProperty);
            if (converter != null)
                value = converter.Convert(value, selectionProperty.PropertyType, null, CultureInfo.CurrentCulture);

            var cmd = new ViewModel.SetFormatPropertyCommand(ApplicationWindow.StackManager.ActiveUndoStack, ApplicationWindow.StackManager.ActiveRedoStack)
            {
                Property = selectionProperty,
                Selection = selection,
                Value = value
            };

            cmd.Execute(parameter);
            interior.Selection = selection;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets the value indicating whether the command is currently
        /// writing a value back to the control.
        /// </summary>
        public bool IsWritingPropertyBack
        {
            get;
            private set;
        }

        #endregion

        #region Private Fields

        /// <summary>
        /// Holds the control that is being controlled by this command.
        /// </summary>
        private Control control;
        /// <summary>
        /// Holds the property that is being set by this command on the control.
        /// </summary>
        private DependencyProperty controlProperty;
        /// <summary>
        /// Holds the converter.
        /// </summary>
        private IValueConverter converter;
        /// <summary>
        /// Holds the current interior.
        /// </summary>
        private ViewModel.Elements.RtfTextSectionInterior interior;
        /// <summary>
        /// Holds the formating property that is being set by this command.
        /// </summary>
        private DependencyProperty selectionProperty;

        #endregion
    }
}
